package io.github.yfblock.frame;

import io.github.yfblock.frame.Annotations.External;
import io.github.yfblock.frame.Core.DispatcherServlet;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.Tomcat;

import javax.servlet.ServletException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class BootApplication {

    // 解压jar包
    public static void unzipJar(String destinationDir, String jarPath) throws IOException {
        File file = new File(jarPath);
        JarFile jar = new JarFile(file);

        byte[] buffer = new byte[4096];
        int len = -1;

        // 解压所有文件
        for (Enumeration<JarEntry> enums = jar.entries(); enums.hasMoreElements();) {
            JarEntry entry = enums.nextElement();

            if(!entry.getName().startsWith("static/")) continue;    // 仅解压static文件夹

            String fileName = destinationDir + File.separator + entry.getName();
            File f = new File(fileName);

            if (!fileName.endsWith("/") && !fileName.endsWith(".class")) {
                f.getParentFile().mkdirs(); // 如果文件存在 构建路径

                InputStream is = jar.getInputStream(entry);
                FileOutputStream fos = new FileOutputStream(f);

                while ((len = is.read(buffer)) != -1) {
//                    is.read(buffer);
                    fos.write(buffer, 0, len);
                }

                // only for version above java 11
//                fos.write(is.readAllBytes());

                fos.close();
                is.close();
            }
        }
    }

    /**
     * 服务初始化函数
     * @throws ServletException servlet异常
     * @throws LifecycleException 时间周期异常
     * @throws IOException  读写异常
     */
    public void starter() throws ServletException, LifecycleException, IOException {
        this.starter(BootApplication.class);
    }

    /**
     * 服务初始化函数
     * @param BootAppClass 启动函数类
     * @throws ServletException servlet异常
     * @throws LifecycleException 时间周期异常
     * @throws IOException  读写异常
     */
    public void starter(Class<?> BootAppClass) throws LifecycleException, IOException, ServletException {
        // 启动 tomcat
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(Integer.getInteger("port", 8080));
        tomcat.getConnector();

        // 获取项目路径
        String path = this.getClass().getProtectionDomain().getCodeSource().getLocation().getFile();

        String docBase = path;  // 静态资源文件夹

        // 如果是jar包 则解压资源文件
        if(path.endsWith(".jar")) {
            unzipJar("./webapp", path);
            System.out.println("解压完毕");
        }
        docBase = (new File(path)).getParent();

        // 配置Servlet
        Context ctx = tomcat.addContext("", docBase);
        Wrapper wrapper = ctx.createWrapper();
        wrapper.setName("DispatcherServlet");
        wrapper.setServletClass(DispatcherServlet.class.getName());
        if(!BootAppClass.equals(BootApplication.class)) {
            String debugPath = BootAppClass.getProtectionDomain().getCodeSource().getLocation().getFile();
            String packageName = BootAppClass.getPackage().getName();
            String external = null;
            if(BootAppClass.getAnnotation(External.class)!=null) {
                external = BootAppClass.getAnnotation(External.class).value();
            }
            System.out.println("==============================================");
            System.out.println("调试模块路径:" + debugPath);
            System.out.println("调试模块包名:" + packageName);
            System.out.println("调试模块地址:" + external);
            System.out.println("==============================================");

            if(external==null)  {
                System.out.println("Error: 未设置模块路径");
                return;
            }

            wrapper.addInitParameter("debugPath", debugPath);
            wrapper.addInitParameter("debugPackage", packageName);
            wrapper.addInitParameter("debugUrl", external);
        }
        wrapper.setLoadOnStartup(1);
        ctx.addChild(wrapper);
        ctx.addServletMappingDecoded("/", "DispatcherServlet");

        // 配置静态资源
        Wrapper defaultServlet = ctx.createWrapper();
        defaultServlet.setName("default");
        defaultServlet.setServletClass("org.apache.catalina.servlets.DefaultServlet");
        defaultServlet.addInitParameter("debug", "0");
        defaultServlet.addInitParameter("listings", "false");
        defaultServlet.setLoadOnStartup(1);
        ctx.addChild(defaultServlet);
        ctx.addServletMappingDecoded("/static/*", "default");

        Commander commander = new Commander();
        commander.run();

        // 开启服务器
        tomcat.start();
        tomcat.getServer().await();
    }

    public static void main(String[] args) throws LifecycleException, IOException, ServletException {
        BootApplication bootApplication = new BootApplication();
        bootApplication.starter();
    }

}
